#ifndef __MY_FILE_MANAGE__
#define __MY_FILE_MANAGE__

#include <iostream>
#include <string>
#include <time.h>
#include <sys/stat.h>
#include <vector>
#include <fstream>
#include <filesystem>
#include <unistd.h>
#include <experimental/filesystem>
#include "bundle.h"
#include "jsoncpp/json/json.h"

// const std::string dict = "./hello.txt";
// const int mode = 0777;

namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class filemanage
    {
    public:
        filemanage(const std::string &filename)
            : _filename(filename)
        {
            if (Exists())
            {
                // stat:获取文件状态属性信息
                if (stat(_filename.c_str(), &status) < 0)
                {
                    perror("stat fail");
                    exit(-1);
                }
            }
        }
        ~filemanage()
        {
        }
        std::string FileName()
        {
            size_t pos = _filename.rfind("/");
            if (pos == std::string::npos)
            {
                return _filename;
            }

            return _filename.substr(pos + 1);
        }
        // 获取文件大小
        int64_t FileSize()
        {
            // // stat:获取文件状态属性信息
            // if (stat(_filename.c_str(), &status) < 0)
            // {
            //     perror("stat fail");
            //     exit(-1);
            // }

            return status.st_size;
        }
        // 获取文件最后一次修改时间
        time_t LastMTime()
        {
            // // stat:获取文件状态属性信息
            // if (stat(_filename.c_str(), &status) < 0)
            // {
            //     perror("stat fail");
            //     exit(-1);
            // }

            return status.st_mtime;
        }
        // 获取文件最后一次访问时间
        time_t LastATime()
        {
            // // stat:获取文件状态属性信息
            // if (stat(_filename.c_str(), &status) < 0)
            // {
            //     perror("stat fail");
            //     exit(-1);
            // }
            return status.st_atime;
        }
        // 向文件写入数据
        bool SetContent(const std::string &body)
        {
            // 打开文件
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            // std::ios_base::in | std::ios_base::binary
            // ofs.open(_filename,std::ios_base::in | std::ios_base::binary);
            if (ofs.is_open() == false)
            {
                perror("open fail");
                return false;
            }

            // std::cout << "-----------------------------" << std::endl;
            // std::cout << "文件名：" << _filename << std::endl;
            // std::cout << "-----------------------------" << std::endl;

            // 向文件写入数据
            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                perror("write fail");
                ofs.close();
                return false;
            }

            ofs.close();
            return true;
        }
        // 从文件中获取数据
        bool GetContent(std::string &body)
        {
            size_t size = this->FileSize();
            // std::cout << _filename << ": " << size << std::endl;

            return GetPosLen(body, 0, size);
        }
        // 获取文件指定位置，指定长度的数据
        bool GetPosLen(std::string &body, size_t pos, size_t len)
        {
            size_t size = this->FileSize();
            if (pos + len > size)
            {
                std::cout << "pos and len error" << std::endl;
                return false;
            }
            body.resize(len);

            std::ifstream ifs;
            // 打开文件
            ifs.open(_filename, std::ios::binary);
            // std::cout << "文件名：" << _filename << std::endl;

            if (ifs.is_open() == false)
            {
                perror("open fail");
                return false;
            }
            // 跳转到指定位置
            ifs.seekg(pos, std::ios::beg);
            // 从指定位置开始读取数据到body
            ifs.read(&body[0], len);
            if (ifs.good() == false)
            {
                perror("read fail");
                std::cout << _filename << ": read fail" << std::endl;
                std::cout << _filename << "size: " << this->FileSize() << std::endl;
                ifs.close();
                return false;
            }

            ifs.close();
            return true;
        }
        // 判断文件是否存在
        bool Exists()
        {
            // // C++17之前
            // std::ifstream ifs(_filename);
            // return ifs.good();

            // C++17之后
            return fs::exists(_filename);
        }
        // 创建目录
        bool CreateDirectory()
        {
            // 方法一：
            // // mkdir:创建目录
            // int mk = mkdir(&dict[0],mode);
            // if(mk == 0)
            // {
            //     std::cout << "目录创建成功" << std::endl;
            //     return true;
            // }
            // else
            // {
            //     perror("mkdir fail");
            //     return false;
            // }

            // 方法二：
            if (Exists())
                return true;
            // create_directory:如果父目录不存在，则创建失败
            // create_directories:如果父目录不存在，则创建，知道创建目标目录
            return fs::create_directories(_filename);
        }
        // 获取目录
        bool GetDirectory(std::vector<std::string> &array)
        {
            for (auto &ft : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(ft))
                    continue;

                // 相对路径还是绝对路径取决于给的参数
                array.push_back(ft.path());

                // // relative_path：获取相对路径
                // array.push_back(fs::path(ft).relative_path());
            }
            return true;
        }
        bool Romove()
        {            
            if (this->Exists() == true)
            {
                std::cout << "remove: " << _filename.c_str() << std::endl;
                remove(_filename.c_str());
            }

            return true;
        }
        // 压缩文件
        bool Compress(const std::string &packname)
        {
            std::string str;

            // 获取数据
            if (GetContent(str) == false)
            {
                std::cout << "Compress get file content failed!\n";
                return false;
            }
            // 压缩数据
            std::string pack = bundle::pack(bundle::LZIP, str);
            // 写入数据
            filemanage fm(packname);
            if (fm.SetContent(pack) == false)
            {
                std::cout << "Compress set file content failed!\n";
                return false;
            }

            return true;
        }
        // 解压缩文件
        bool UnCompress(const std::string &filename)
        {
            std::string str;
            // 获取数据
            if (GetContent(str) == false)
            {
                std::cout << "UnCompress get file content failed!\n";
                return false;
            }
            // 解压数据
            std::string unpack = bundle::unpack(str);
            // 写入数据
            filemanage fm(filename);
            if (fm.SetContent(unpack) == false)
            {
                std::cout << "UnCompress set file content failed!\n";
                return false;
            }

            return true;
        }

    private:
        std::string _filename;
        struct stat status;
    };

    class Jsonmanage
    {
    public:
        static bool Serialize(const Json::Value &root, std::string &str)
        {
            Json::StreamWriterBuilder swb;
            swb["emitUTF8"] = true;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            std::stringstream ss;
            if (sw->write(root, &ss) != 0)
            {
                std::cout << "json write failed!\n";
                return false;
            }

            str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str, Json::Value &root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
            if (ret == false)
            {
                std::cout << "parse error:" << err << std::endl;
                return false;
            }

            return true;
        }
    };
}

#endif